package com.jddai.arithmetic;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class BRBInference {

    private File dataFile;
    private File modelFile;
    private ArrayList<Double> uscale;

    private boolean choose;
    private double contribution_per;
    private int contribution_num;


    private ArrayList<Double> Inference_Result;
    private ArrayList<ArrayList<Double>> dataArray = new ArrayList<ArrayList<Double>>();
    private ArrayList<ArrayList<Double>> modelArray = new ArrayList<ArrayList<Double>>();

    ArrayList<ArrayList<Double>> data_weights = new ArrayList<ArrayList<Double>>();
    ArrayList<ArrayList<Integer>> Rule_Actived = new ArrayList<ArrayList<Integer>>();
    ArrayList<ArrayList<Double>> data_Scaleutility = new ArrayList<ArrayList<Double>>();

    ArrayList<ArrayList<ArrayList<Double>>> contr_alpha = new ArrayList<ArrayList<ArrayList<Double>>>();  //
    ArrayList<ArrayList<Double>> contribution = new ArrayList<ArrayList<Double>>(); //第6-11个前提属性对结果的贡献
    public ArrayList<ArrayList<Integer>> data_attr = new ArrayList<ArrayList<Integer>>();  //对结果贡献度大于80%的前提属性的序号(6-11之间)
    public ArrayList<Double> contri_sum = new ArrayList<Double>();  //大于80%的具体贡献度 总的
    public ArrayList<ArrayList<Double>> contri_indivi = new ArrayList<ArrayList<Double>>(); //大于80%的具体贡献度 分开

    public ArrayList<Double> percent_contri_sum = new ArrayList<Double>();  //大于80%的具体贡献度 总的
    public ArrayList<ArrayList<Double>> percent_contri_indivi = new ArrayList<ArrayList<Double>>(); //大于80%的具体贡献度 分开

    public BRBInference() {
    }

    public BRBInference(File dataFile, File modelFile, ArrayList<Double> uscale) {
        this.dataFile = dataFile;
        this.modelFile = modelFile;
        this.uscale = uscale;
    }

    public BRBInference(ArrayList<Double> uscale, ArrayList<ArrayList<Double>> dataArray, ArrayList<ArrayList<Double>> modelArray) {
        this.uscale = uscale;
        this.dataArray = dataArray;
        this.modelArray = modelArray;
    }

    public ArrayList<ArrayList<Double>> getDataArray() {
        return dataArray;
    }

    public ArrayList<ArrayList<Double>> getModelArray() {
        return modelArray;
    }

    public ArrayList<ArrayList<Integer>> getData_attr() {
        return data_attr;
    }

    public ArrayList<Double> getInference_Result() {
        return Inference_Result;
    }

    public void setContribution_per(double contribution_per) {
        this.contribution_per = contribution_per/100;
    }

    public void setChoose(boolean choose) {
        this.choose = choose;
    }

    public void setContribution_num(int contribution_num) {
        this.contribution_num = contribution_num;
    }

    public static ArrayList<ArrayList<Double>> loadData(File file) throws IOException {   //上传文件
        BufferedReader bf = new BufferedReader(new FileReader(file));
        ArrayList<ArrayList<Double>> dataArray = new ArrayList<ArrayList<Double>>();
        String line;
        int num = 0;
        while ((line = bf.readLine()) != null) {
            String[] ss = line.split("\t");
            ArrayList<Double> d = new ArrayList<Double>();
            for (int i = 0; i < ss.length; i++) {
                d.add(Double.parseDouble(ss[i]));
            }
            dataArray.add(d);
            num++;
        }
        bf.close();
        return dataArray;
    }

    public void Load() throws IOException {
        dataArray = BRBInference.loadData(this.dataFile);
        modelArray = BRBInference.loadData(this.modelFile);
    }

    public static Double single_round(Double input) {
            double temp = input * 100000;
            int temp1 = (int) (input * 10000);
            double temp2 = (((temp % 10000) % 1000) % 100) % 10;
            if(temp2>=5.0) {
                temp1 += 1;
            }
            double output = (double) temp1 / 10000.0;

        return output;
    }

    public static ArrayList<Double> round(ArrayList<Double> input) {
        ArrayList<Double> output = new ArrayList<Double>();

        for(int i=0;i<input.size();i++) {
            double temp = input.get(i) * 100000;
            int temp1 = (int) (input.get(i) * 10000);
            double temp2 = (((temp % 10000) % 1000) % 100) % 10;
            if(temp2>=5.0) {
                temp1 += 1;
            }

            double d = (double) temp1 / 10000.0;

            output.add(d);

            System.out.println();
        }

        return output;
    }


    public static ArrayList<ArrayList<Double>> Round(ArrayList<ArrayList<Double>> input) {
        ArrayList<ArrayList<Double>> output = new ArrayList<ArrayList<Double>>();

        for(int i=0;i<input.size();i++) {
            ArrayList<Double> Temp = new ArrayList<Double>();
            for(int j=0;j<input.get(i).size();j++) {
                double temp = input.get(i).get(j) * 100000.0;
                int temp1 = (int) (input.get(i).get(j) * 10000);
                double temp2 = (((temp % 10000) % 1000) % 100) % 10;
                if(temp2>=5.0) {
                    temp1 += 1;
                }
                double  d = (double) temp1 / 10000.0;

                Temp.add(d);
            }
            output.add(Temp);
        }

        return output;
    }

    public ArrayList<Double> per_round(ArrayList<Double> input) {
        ArrayList<Double> output = new ArrayList<Double>();

        for(int i=0;i<input.size();i++) {
            double temp = input.get(i) * 1000;
            int temp1 = (int) (input.get(i) * 100);
            double temp2 = (temp % 100) % 10;
            if(temp2>=5.0) {
                temp1 += 1;
            }

            double d = (double) temp1 / 100.0;

            output.add(d);

            System.out.println();
        }

        return output;
    }

    public ArrayList<ArrayList<Double>> Per_Round(ArrayList<ArrayList<Double>> input) {
        ArrayList<ArrayList<Double>> output = new ArrayList<ArrayList<Double>>();

        for(int i=0;i<input.size();i++) {
            ArrayList<Double> Temp = new ArrayList<Double>();
            for(int j=0;j<input.get(i).size();j++) {
                double temp = input.get(i).get(j) * 1000.0;
                int temp1 = (int) (input.get(i).get(j) * 100);
                double temp2 = (temp % 100) % 10;
                if(temp2>=5.0) {
                    temp1 += 1;
                }
                double  d = (double) temp1 / 100.0;

                Temp.add(d);
            }
            output.add(Temp);
        }

        return output;
    }


    public ArrayList<Double> percent(ArrayList<Double> input) {
        ArrayList<Double> output = new ArrayList<Double>();
        for(int i=0;i<input.size();i++) {
            double temp = input.get(i) * 100.0;
            output.add(temp);
        }
        return output;
    }

    public ArrayList<ArrayList<Double>> Percent(ArrayList<ArrayList<Double>> input) {
        ArrayList<ArrayList<Double>> output = new ArrayList<ArrayList<Double>>();

        for(int i=0;i<input.size();i++) {
            ArrayList<Double> Temp = new ArrayList<Double>();
            for(int j=0;j<input.get(i).size();j++) {
                double temp = input.get(i).get(j) * 100.0;
                Temp.add(temp);
            }
            output.add(Temp);
        }

        return output;
    }


    public static Double max(ArrayList<Double> data) {   //求最大值
        Double max = data.get(0);
        for (int i = 1; i < data.size(); i++) {
            if (max < data.get(i)) {
                max = data.get(i);
            }
        }
        return max;
    }

    public static int find_max(ArrayList<Double> data) {   //求最大值
        int flag = 0;
        Double max = data.get(0);
        for (int i = 1; i < data.size(); i++) {
            if (max < data.get(i)) {
                max = data.get(i);
            }
        }
        for (int i = 1; i < data.size(); i++) {
            if (data.get(i) == max) {
                flag = i;
                break;
            }
        }
        return flag;
    }

    public static int find_min(ArrayList<Double> data) {   //寻找最大值
        int flag = 0;
        Double min = data.get(0);
        for (int i = 1; i < data.size(); i++) {
            if (min > data.get(i)) {
                min = data.get(i);
            }
        }
        for (int i = 1; i < data.size(); i++) {
            if (data.get(i) == min) {
                flag = i;
                break;
            }
        }
        return flag;
    }

    public static Double min(ArrayList<Double> data) {  //求最小值
        Double min = data.get(0);
        for (int i = 1; i < data.size(); i++) {
            if (min > data.get(i)) {
                min = data.get(i);
            }
        }
        return min;
    }

    public static Double sum(ArrayList<Double> data) {  //求和
        Double sum = 0.0;
        for (Double value : data) {
            sum += value;
        }
        return sum;
    }

    public static Double sum(Double[] data) {  //求和
        Double sum = 0.0;
        for (Double value : data) {
            sum += value;
        }
        return sum;
    }

    public static ArrayList<Double> abs(ArrayList<Double> data) {  //绝对值
        ArrayList<Double> data2 = new ArrayList<Double>();
        for (Double value : data) {
            if (value < 0) {
                value = -value;
            }
            data2.add(value);
        }
        return data2;
    }

    public static Double abs(Double data) {
        if (data < 0) {
            return -data;
        } else {
            return data;
        }
    }

    public ArrayList<Integer> flag0(ArrayList<Double> data) {    //找寻0附近值
        ArrayList<Integer> match = new ArrayList<Integer>();
        ArrayList<Double> more = new ArrayList<Double>();
        ArrayList<Double> less = new ArrayList<Double>();
        for (int i = 0; i < data.size(); i++) {
            if (data.get(i) > 0) {
                more.add(data.get(i));
            }
            if (data.get(i) == 0) {
                match.add(i);
                match.add(i);
                return match;
            }
            if (data.get(i) < 0) {
                less.add(data.get(i));
            }
        }
        Double m1 = min(more);
        Double m2 = max(less);
        for (int i = 0; i < data.size(); i++) {
            if (data.get(i).equals(m1)) {
                match.add(i);
            }
            if (data.get(i).equals(m2) ) {
                match.add(i);
            }
        }
        return match;
    }

    public static Double comprod(ArrayList<Double> data) {  //阶乘
        Double temp = 1.0;
        for (Double value : data) {
            temp *= value;
        }
        return temp;
    }

    public static ArrayList<ArrayList<Double>> transp(ArrayList<ArrayList<Double>> input) {  //转置
        ArrayList<ArrayList<Double>> output = new ArrayList<ArrayList<Double>>();
        for (int j = 0; j < input.get(0).size(); j++) {
            ArrayList<Double> temp = new ArrayList<Double>();
            for (int i = 0; i < input.size(); i++) {
                temp.add(input.get(i).get(j));
            }
            output.add(temp);
        }
        return output;
    }

    public void attr_identify() throws IOException {

        for (int num = 0; num < dataArray.size(); num++) {
            ArrayList<Double> data = dataArray.get(num);

            ArrayList<ArrayList<Double>> abstr = new ArrayList<ArrayList<Double>>();

            for (int i = 0; i < data.size() - 1; i++) {
                ArrayList<Double> ab = new ArrayList<Double>();
                for (ArrayList<Double> rule : modelArray) {
                    ab.add(data.get(i) - rule.get(i + 1));
                }
                abstr.add(ab);
            }

            ArrayList<ArrayList<Integer>> match = new ArrayList<ArrayList<Integer>>();

            for (ArrayList<Double> temp : abstr) {
                ArrayList<Integer> flag0 = new ArrayList<Integer>();
                for (Double value : temp) {
                    flag0 = flag0(temp);
                }
                match.add(flag0);
            }

            ArrayList<Double> grap = new ArrayList<Double>();
            for (int i = 0; i < data.size() - 1; i++) {
                grap.add(modelArray.get(match.get(i).get(0)).get(i + 1) - modelArray.get(match.get(i).get(1)).get(i + 1));
            }
            grap = abs(grap);

            ArrayList<ArrayList<Double>> degree = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < data.size() - 1; i++) {
                ArrayList<Double> de = new ArrayList<Double>();
                for (int j = 0; j < modelArray.size(); j++) {
                    if (match.get(i).get(0) == j && match.get(i).get(1) == j) {
                        de.add(1.0);
                    } else if (match.get(i).get(0) == j || match.get(i).get(1) == j) {
                        de.add(1 - (abs(data.get(i) - modelArray.get(j).get(i + 1))) / grap.get(i));
                    } else {
                        de.add(0.0);
                    }
                }
                degree.add(de);
            }

            ArrayList<Double> weights = new ArrayList<Double>();
            for (int i = 0; i < modelArray.size(); i++) {
                Double sum = 0.0;
                for (ArrayList<Double> de : degree) {
                    sum = sum + de.get(i);
                }
                weights.add(sum);
            }

            ArrayList<ArrayList<Double>> contribution = new ArrayList<ArrayList<Double>>();
            for (ArrayList<Double> de : degree) {
                ArrayList<Double> con = new ArrayList<Double>();
                for (int i = 0; i < de.size(); i++) {
                    if(weights.get(i)!=0) {
                        con.add(de.get(i) / weights.get(i));
                    }
                }
                contribution.add(con);
            }

            ArrayList<Double> weight_original = new ArrayList<Double>();
            for (ArrayList<Double> rule : modelArray) {
                weight_original.add(rule.get(0));
            }

            ArrayList<Double> weights2 = new ArrayList<Double>();   //乘以原始权重
            for (int i = 0; i < weights.size(); i++) {
                weights2.add(weights.get(i) * weight_original.get(i));
            }

            Double sum = 0.0;
            for (Double weight : weights2) {
                sum += weight;
            }

            ArrayList<Double> weights3 = new ArrayList<Double>();   //归一化权重
            for (Double weight : weights2) {
                weights3.add(weight / sum);
            }

            ArrayList<Integer> rule_actived = new ArrayList<Integer>();
            for (int count = 0; count < weights3.size(); count++) {
                if (weights3.get(count) != 0) {
                    rule_actived.add(count);
                }
            }

            this.data_weights.add(weights3);
            this.contr_alpha.add(contribution);
            this.Rule_Actived.add(rule_actived);

        }

    }

    public void fuse() {
        int size_scales = modelArray.get(0).size() - dataArray.get(0).size();
        ArrayList<Double> Result = new ArrayList<Double>();

        for (int num = 0; num < dataArray.size(); num++) {
            ArrayList<ArrayList<Double>> scales_actived = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < Rule_Actived.get(num).size(); i++) {
                ArrayList<Double> ru_sc = new ArrayList<Double>();
                for (int j = dataArray.get(num).size(); j < modelArray.get(i).size(); j++) {
                    ru_sc.add(modelArray.get(Rule_Actived.get(num).get(i)).get(j));
                }
                scales_actived.add(ru_sc);
            }

            ArrayList<ArrayList<Double>> M = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < scales_actived.size(); i++) {
                ArrayList<Double> m = new ArrayList<Double>();
                for (int j = 0; j < scales_actived.get(i).size(); j++) {
                    m.add(data_weights.get(num).get(i) * scales_actived.get(i).get(j));
                }
                M.add(m);
            }

            ArrayList<Double> M_R = new ArrayList<Double>();
            ArrayList<Double> M_R_ = new ArrayList<Double>();

            for (Double value : data_weights.get(num)) {
                M_R.add(1 - value);
                M_R_.add(1 - value);
            }

            ArrayList<ArrayList<Double>> An = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < M.size(); i++) {
                ArrayList<Double> an = new ArrayList<Double>();
                for (Double value : M.get(i)) {
                    an.add(value + M_R_.get(i));
                }
                An.add(an);
            }

            ArrayList<ArrayList<Double>> AnT = transp(An);

            ArrayList<Double> miu1 = new ArrayList<Double>();
            for (ArrayList<Double> data : AnT) {
                miu1.add(comprod(data));
            }

            Double miu2 = comprod(M_R_);

            double miu = 1 / (sum(miu1) - (size_scales - 1) * miu2);

            ArrayList<Double> m_k = new ArrayList<Double>();
            for (Double value : miu1) {
                m_k.add(miu * (value - miu2));
            }
            double m_H_ = miu * miu2;

            ArrayList<Double> gt = new ArrayList<Double>();
            for (Double value : m_k) {
                gt.add(value / (1 - m_H_));
            }

            ArrayList<Double> scaleutility = new ArrayList<Double>();

            for (int i = 0; i < uscale.size(); i++) {
                scaleutility.add(uscale.get(i) * gt.get(i));
            }

            Double single = sum(scaleutility);

            Result.add(single);
            data_Scaleutility.add(scaleutility);
        }

        Inference_Result = Result;

        System.out.println();
    }

    public void contr_w_beta() {

        for (int num = 0; num < dataArray.size(); num++) {

            ArrayList<Double> weight = data_weights.get(num);

            ArrayList<ArrayList<Double>> scales_actived = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < Rule_Actived.get(num).size(); i++) {
                ArrayList<Double> ru_sc = new ArrayList<Double>();
                for (int j = dataArray.get(num).size(); j < modelArray.get(i).size(); j++) {
                    ru_sc.add(modelArray.get(Rule_Actived.get(num).get(i)).get(j));
                }
                scales_actived.add(ru_sc);
            }

            int size_beta1 = scales_actived.size();
            int size_beta2 = scales_actived.get(0).size();

            ArrayList<Double> part1 = new ArrayList<Double>();
            for (int i = 0; i < size_beta2; i++) {
                part1.add(1.0);
            }

            Double part2 = 1.0;

            for (int i = 0; i < scales_actived.size(); i++) {
                for (int j = 0; j < scales_actived.get(i).size(); j++) {
                    part1.set(j, part1.get(j) * (weight.get(i) * scales_actived.get(i).get(j) + 1.0 - weight.get(i)));
                }
                part2 = part2 * (1 - weight.get(i));
            }

            ArrayList<Double> f = new ArrayList<Double>();
            for (Double value : part1) {
                f.add(value - part2);
            }

            Double g = sum(part1) - size_beta2 * part2;

            ArrayList<ArrayList<Double>> part_new1 = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < size_beta1; i++) {
                ArrayList<Double> temp = new ArrayList<Double>();
                for (int j = 0; j < size_beta2; j++) {
                    temp.add(part1.get(j) * (scales_actived.get(i).get(j) - 1) / (weight.get(i) * scales_actived.get(i).get(j) + 1.0 - weight.get(i)));
                }
                part_new1.add(temp);
            }

            ArrayList<ArrayList<Double>> contr_w_beita_d1 = new ArrayList<ArrayList<Double>>();
            for (int i = 0; i < size_beta1; i++) {

                ArrayList<Double> temp = new ArrayList<Double>();
                for (int j = 0; j < size_beta2; j++) {
                    double fw = part_new1.get(i).get(j) + part2 / (1.0 - weight.get(i));
                    double gw = sum(part_new1.get(i)) + size_beta2 * part2 / (1.0 - weight.get(i));
                    double fbeita = part1.get(j) * weight.get(i) / (weight.get(i) * scales_actived.get(i).get(j) + 1 - weight.get(i));
                    double gbeita = fbeita;
                    double weight_d = fw / g - f.get(j) / g / g * gw;
                    double beita_d = fbeita / g - f.get(j) / g / g * gbeita;
                    double contr_w_beita_add_multiply = abs(weight_d) + beita_d - abs(weight_d * beita_d);
                    temp.add(contr_w_beita_add_multiply * beita_d);
                }
                contr_w_beita_d1.add(temp);
            }

            ArrayList<ArrayList<Double>> contr_w_beita_d2 = transp(contr_w_beita_d1);
            ArrayList<ArrayList<Double>> contr_w_beita_d3 = new ArrayList<ArrayList<Double>>();
            for (ArrayList<Double> value : contr_w_beita_d2) {
                ArrayList<Double> temp = new ArrayList<Double>();
                for (int j = 0; j < value.size(); j++) {
                    temp.add(value.get(j) / sum(value));
                }
                contr_w_beita_d3.add(temp);
            }

            ArrayList<ArrayList<Double>> contr_w_beita = transp(contr_w_beita_d3);

            ArrayList<Double> contr_final_beita = new ArrayList<Double>();
            for (int i = 0; i < data_Scaleutility.get(num).size(); i++) {
                contr_final_beita.add(data_Scaleutility.get(num).get(i) / Inference_Result.get(num));
            }

            ArrayList<Double> Temp = new ArrayList<Double>();
            for (int i = 0; i < contr_w_beita.size(); i++) {
                double temp = 0.0;
                for (int j = 0; j < contr_w_beita.get(i).size(); j++) {
                    temp += contr_final_beita.get(j) * contr_w_beita.get(i).get(j);
                }
                Temp.add(temp);
            }
            ArrayList<ArrayList<Double>> contr_alpha_temp = contr_alpha.get(num);

            ArrayList<Double> contri = new ArrayList<Double>();
            for (int i = 0; i < contr_alpha_temp.size(); i++) {
                double temp = 0.0;
                for (int j = 0; j < contr_alpha_temp.get(i).size(); j++) {
                    temp += Temp.get(j) * contr_alpha_temp.get(i).get(j);
                }
                contri.add(temp);
            }

            Double sum_temp = 0.0;
            for(int i=5;i<11;i++) {
                sum_temp += contri.get(i);
            }

            ArrayList<Double> contribution_temp = new ArrayList<Double>();
            for(int i=5;i<11;i++) {
                contribution_temp.add(contri.get(i)/sum_temp);
            }

            contribution.add(contribution_temp);

        }

        ArrayList<ArrayList<Double>> contribution_temp = new ArrayList<ArrayList<Double>>();

        for(int i=0;i<contribution.size();i++) {
            ArrayList<Double> temp = new ArrayList<Double>();
            for(int j=0; j<contribution.get(i).size();j++) {
                temp.add(contribution.get(i).get(j));
            }
            contribution_temp.add(temp);
        }

        for (int num = 0; num < dataArray.size(); num++) {

            contri_sum.add(0.0);
            int number = 0;


            ArrayList<Integer> attr_temp = new ArrayList<Integer>();
            ArrayList<Double> attr_con_temp = new ArrayList<Double>();

            if(choose) {
                while (contri_sum.get(num)<contribution_per) {
                    contri_sum.set(num,contri_sum.get(num)+max(contribution_temp.get(num)));
                    attr_temp.add(find_max(contribution_temp.get(num))+6);
                    attr_con_temp.add(max(contribution_temp.get(num)));
                    contribution_temp.get(num).set(attr_temp.get(number)-6,0.0);
                    number += 1;
                }
            } else {
                for(int k=0;k<contribution_num;++k) {
                    contri_sum.set(num,contri_sum.get(num)+max(contribution_temp.get(num)));
                    attr_temp.add(find_max(contribution_temp.get(num))+6);
                    attr_con_temp.add(max(contribution_temp.get(num)));
                    contribution_temp.get(num).set(attr_temp.get(k)-6,0.0);
                }

            }

            data_attr.add(attr_temp);
            contri_indivi.add(attr_con_temp);

        }
        System.out.println();
    }

    public void data_Handle(){

        contri_sum = percent(contri_sum);
        contri_indivi = Percent(contri_indivi);

        percent_contri_sum = per_round(contri_sum);
        percent_contri_indivi = Per_Round(contri_indivi);
        Inference_Result = round(Inference_Result);
        Inference_Result = round(Inference_Result);
    }


}
